<?
/**
 * Copyright 2007 Melange.
 *
 * This file is part of PHP-AAS.
 *
 * PHP-AAS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP-AAS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP-AAS; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category    Melange
 * @package     php-aas
 * @subpackage  realm
 * @copyright   Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @author      Jeroen Simons <jeroen@melange.nl>
 * @link        http://www.melange.nl/
 *
 */

require_once("aas/RealmBase.php");

/**
 * Simple implementation of <b>Realm</b> that reads an XML file to configure
 * the valid users, passwords, and roles.  The file format (and default file
 * location) are identical to those currently supported by Tomcat 3.X.
 * <p>
 * <strong>IMPLEMENTATION NOTE</strong>: It is assumed that the in-memory
 * collection representing our defined users (and their roles) is initialized
 * at application startup and never modified again.  Therefore, no thread
 * synchronization is performed around accesses to the principals collection.
 */

class MemoryRealm extends RealmBase {


    private $log;

    /**
     * Descriptive information about this Realm implementation.
     */

    protected $name = "MemoryRealm";


    /**
     * The pathname (absolute or relative to Catalina's current working
     * directory) of the XML file containing our database information.
     */
    private $pathname = "resources/phpmvc-users.xml";


    /**
     * The set of valid Principals for this Realm, keyed by user name.
     */
    private $principals = array();


    public function __construct() {
        parent::__construct();
        $this->log =& LoggerManager::getLogger("MemoryRealm");
    }


    // ------------------------------------------------------------- Properties

    /**
     * Return the pathname of our XML file containing user definitions.
     */
    public function getPathname() {

        return $this->pathname;

    }


    /**
     * Set the pathname of our XML file containing user definitions.  If a
     * relative pathname is specified, it is resolved against "catalina.base".
     *
     * @param pathname The new pathname
     */
    public function setPathname($pathname) {

        $this->pathname = $pathname;

    }


    // --------------------------------------------------------- Public Methods


    /**
     * Return the Principal associated with the specified username and
     * credentials, if there is one; otherwise return <code>null</code>.
     *
     * @param username Username of the Principal to look up
     * @param credentials Password or other credentials to use in
     *  authenticating this username
     */
    public function authenticate($username, $credentials) {

        $principal = array_key_exists($username, $this->principals) ?
            $this->principals[$username] : null;
        $validated = false;

        if ($principal !== null) {
            if ($this->hasMessageDigest()) {
                // Hex hashes should be compared case-insensitive
                $validated = strtolower($this->digest($credentials)) ==
                        strtolower($principal->getPassword());
            } else {
                $validated = ($this->digest($credentials) == $principal->getPassword());
            }
        }

        if ($validated) {
            if($this->log->isDebugEnabled())
                $this->log->debug("Username ".$username." successfully authenticated.");
            return ($principal);
        } else {
            if ($this->log->isDebugEnabled()){
                $this->log->debug("Username ".$username." NOT successfully authenticated.");
                if($principal !== null){
                    $this->log->debug("REASON : passwords didn't match.");
                } else {
                    $this->log->debug("REASON : none of the ".count($this->principals).
                        " principals has provided username.");
                }
            }
            return (null);
        }

    }


    // -------------------------------------------------------- Package Methods


    /**
     * Add a new user to the in-memory database.
     *
     * @param username User's username
     * @param password User's password (clear text)
     * @param roles Comma-delimited set of roles associated with this user
     */
    private function addUser($username, $password, $roles) {

        // Accumulate the list of roles for this user
        $list = array();
        $roles .= ",";
        while (true) {
            $comma = strpos($roles, ",");
            if ($comma === false)
                break;
            $role = trim(substr($roles, 0, $comma));
            $list[] = $role;
            $roles = substr($roles, $comma + 1);
        }

        // Construct and cache the Principal for this user
        $principal =
            new GenericPrincipal($this, $username, $password, $list);
        $this->principals[$username] = $principal;
    }


    // ------------------------------------------------------ Protected Methods


    /**
     * Return a short name for this Realm implementation.
     */
    protected function getName() {
        return ($this->name);
    }


    /**
     * Return the password associated with the given principal's user name.
     */
    protected function getPassword($username) {

        $principal = $this->principals[$username];
        if ($principal !== null) {
            return ($this->principal->getPassword());
        } else {
            return (null);
        }

    }


    /**
     * Return the Principal associated with the given user name.
     */
    protected function getPrincipal($username) {

        return $this->principals[$username];

    }


    // ------------------------------------------------------ Lifecycle Methods


    /**
     * Prepare for active use of the public methods of this Component.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents it from being started
     */
    public function start() {

        //die(var_dump("here"));

        // Construct a reader for the XML input file (if it exists)
        if (!file_exists($this->pathname)) {
            echo "Error while locating the document [".$this->pathname."] \n";
            return;
        }

        $dom = new DomDocument();
        if(!$dom->load($this->pathname)) {
            echo "Error while parsing the document\n";
            exit;
        }

        foreach($dom->getElementsByTagName("user") as $xmluser) {
            $this->addUser(
                $xmluser->getAttribute("username"),
                $xmluser->getAttribute("password"),
                $xmluser->getAttribute("roles")
            );
        }

    }

}
?>